/*
* 日志信息格式化类 格式化信息输出
*/

#ifndef _FORMAT_H_
#define _FORMAT_H_

#include "message.hpp"
#include <iostream>
#include <sstream>
#include <memory>
#include <string>
#include <vector>
#include <ctime>
#include <cassert>

namespace Log
{
    //抽象格式化基类
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>; //基类智能指针类型
        virtual void format(std::ostream& out,LogMsg& msg) = 0; //抽象方法
    };

    //子类类别: 消息,等级,时间,文件名,行号,线程ID,日志器名,制表符,换行,其他

    //消息
    class MsgFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out << msg._payload;
        }
    };

    //日志等级
    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out << LogLevel::toString(msg._level);
        }
    };

    //输出时间
    class TimeFormatItem : public FormatItem
    {
    public:
        //默认格式为 时:分:秒
        TimeFormatItem(const std::string& format = "%H:%M:%S"):_format(format) {
            if(format.empty()) _format = "%H:%M:%S"; //如果用户传递空字符串 则重置内容
        }
        void format(std::ostream& out,LogMsg& msg) override {
            struct tm t;
            localtime_r(&msg._ctime,&t); //将时间戳转化为各种时间写入t对象中
            char tmp[64]; //时间字符串缓冲区
            strftime(tmp,sizeof(tmp)-1,_format.c_str(),&t); //将t中的信息 按照指定格式(%H %M %S...)写入缓冲区中 写入时空出最后一个\0
            out << tmp; //将时间字符串数据写入输出流
        }

    private:
        std::string _format;
    };

    //文件名
    class FileFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out<<msg._file;
        }
    };

    //行号
    class LineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out<<msg._line;
        }
    };

    //线程ID
    class ThreadFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out<<msg._tid;
        }
    };

    //日志器名
    class LoggerFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out<<msg._logger;
        }
    };

    //制表符 间隔空格
    class TabFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out<<"\t";
        }
    };

    //换行
    class NLineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream& out,LogMsg& msg) override {
            out<<"\n";
        }
    };
    
    //其他输出
    class OtherFormatItem : public FormatItem
    {
    public:
        OtherFormatItem(const std::string& other) : _other(other) {}
        void format(std::ostream& out,LogMsg& msg) override {
            out<<_other;
        }

    private:
        std::string _other;
    };


    //日志格式解析类
    /*
    * %d 表示日期 包括子格式{%Y-%m-%d|%H:%M:%S}
    * %t 表示线程ID
    * %c 表示日志器名称
    * %f 表示文件名
    * %l 表示源码行号
    * %p 表示日志级别
    * %m 表示日志主体消息
    * %T 表示缩进
    * %n 表示换行
    */
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string& pattern = "[%d{%Y-%m-%d|%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
        :_pattern(pattern) { assert(parsePattern()); } //构造时调用解析函数 且必须成功

        //对msg进行格式化
        void format(std::ostream& out,LogMsg& msg) 
        {
            //调用对应的格式化函数 传入参数
            for(auto& item:_items)
                item->format(out,msg);
        }
        std::string format(LogMsg& msg) 
        {
            //将io流设置为字符串流 将格式化字符串输出到字符串中返回
            std::stringstream ss;
            format(ss,msg);
            return ss.str();
        }

        //封闭不需要暴露的接口
    private:
        //对格式化规则字符串进行解析
        bool parsePattern() 
        {
            int n = _pattern.size();
            int pos = 0;
            std::vector<std::pair<std::string,std::string>> fmt_arr;
            while(pos < n)
            {
                std::string key;
                std::string val;

                //1.先判断是不是% 如果不是 则是普通字符
                if(_pattern[pos] != '%')
                    val.push_back(_pattern[pos++]);

                //2.到第二步一定是一个% 如果下一个还是%则将%视为一个普通字符
                if(pos + 1 < n && _pattern[pos] == '%' && _pattern[pos+1] == '%')
                {
                    //转义字符 %
                    val.push_back('%');
                    pos += 2;
                }
                //清空普通字符缓冲区
                if(!val.empty())
                {
                    fmt_arr.push_back(std::make_pair("",val));
                    continue;
                }
                //3.如果不是%% 则一定是一个格式化字符%d
                ++pos; //到这里 字符一定是%
                if(pos < n)
                    key.push_back(_pattern[pos++]);
                else
                {
                    std::cout<<"%后无格式化字符,格式错误!\n";
                    return false;
                }
                
                if(pos < n && _pattern[pos] == '{')
                {
                    ++pos;
                    bool safe = true;
                    while(pos < n)
                    {
                        if(_pattern[pos] == '}') 
                        {
                            safe = false;
                            break;
                        }
                        val.push_back(_pattern[pos++]);
                    }
                    if(safe)
                    {
                        std::cout<<"%"<<key[0]<<"{格式化字符串不正确}\n";
                        return false;
                    }
                    ++pos;
                }
                fmt_arr.push_back(std::make_pair(key,val));
                //std::cout<<"key:"<<key<<"|val:"<<val<<std::endl;
            }

            for(const auto& fmt:fmt_arr)
                _items.push_back(createItem(fmt.first,fmt.second));

            return true;
        }
        //根据不同的格式化字符串创建不同的格式化子项对象(生产项)
        //key:指定的日志生产类 val:日志生产类需要的参数
        FormatItem::ptr createItem(const std::string& key,const std::string& val) 
        {
            if(key == "d") return std::make_shared<TimeFormatItem>(val);
            if(key == "t") return std::make_shared<ThreadFormatItem>();
            if(key == "c") return std::make_shared<LoggerFormatItem>();
            if(key == "f") return std::make_shared<FileFormatItem>();
            if(key == "p") return std::make_shared<LevelFormatItem>();
            if(key == "m") return std::make_shared<MsgFormatItem>();
            if(key == "T") return std::make_shared<TabFormatItem>();
            if(key == "l") return std::make_shared<LineFormatItem>();
            if(key == "n") return std::make_shared<NLineFormatItem>();
            if(key == "" || key.empty()) return std::make_shared<OtherFormatItem>(val);

            //走到这里格式化字符有问题
            std::cout<<"不存在的格式化字符%"<<key<<std::endl;
            abort();

            return std::make_shared<OtherFormatItem>(val);
        }
    private:
        std::string _pattern; //格式化规则字符串
        std::vector<FormatItem::ptr> _items; //装配格式字符生产类
    };
}


#endif